/**
 * Copyright (c) 2006-2017, JGraph Ltd
 * Copyright (c) 2006-2017, Gaudenz Alder
 */
TrelloClient = function (editorUi) {
    DrawioClient.call(this, editorUi, 'tauth');
    Trello.setKey(this.key);
};

// Extends DrawioClient
mxUtils.extend(TrelloClient, DrawioClient);

TrelloClient.prototype.key = (window.location.hostname == 'test.draw.io') ?
    'e73615c79cf7e381aef91c85936e9553' : 'e73615c79cf7e381aef91c85936e9553';

TrelloClient.prototype.baseUrl = 'https://api.trello.com/1/';

TrelloClient.prototype.SEPARATOR = '|$|';

/**
 * Maximum attachment size of Trello.
 */
TrelloClient.prototype.maxFileSize = 10000000 /*10MB*/;

/**
 * Default extension for new files.
 */
TrelloClient.prototype.extension = '.xml'; //TODO export to png

/**
 * Authorizes the client, used with methods that can be called without a user click and popup blockers will interfere
 * Show the AuthDialog to work around the popup blockers if the file is opened directly
 */
TrelloClient.prototype.authenticate = function (fn, error, force) {
    if (force) {
        this.logout();
    }

    var callback = mxUtils.bind(this, function (remember, success) {
        Trello.authorize(
            {
                type: 'popup',
                name: 'draw.io',
                scope:
                    {
                        read: 'true',
                        write: 'true'
                    },
                expiration: remember ? 'never' : '1hour',
                success: function () {
                    if (success != null) {
                        success();
                    }

                    fn();
                },
                error: function () {
                    if (success != null) {
                        success();
                    }

                    if (error != null) {
                        error(mxResources.get('loggedOut'));
                    }
                }
            });
    });

    if (this.isAuthorized()) {
        callback(true);
    } else {
        this.ui.showAuthDialog(this, true, callback);
    }
}

/**
 *
 */
TrelloClient.prototype.getLibrary = function (id, success, error) {
    this.getFile(id, success, error, false, true);
};

/**
 *
 */
TrelloClient.prototype.getFile = function (id, success, error, denyConvert, asLibrary) {
    //In getFile only, we
    asLibrary = (asLibrary != null) ? asLibrary : false;

    var callback = mxUtils.bind(this, function () {
        var ids = id.split(this.SEPARATOR);
        var acceptResponse = true;

        var timeoutThread = window.setTimeout(mxUtils.bind(this, function () {
            acceptResponse = false;
            error({code: App.ERROR_TIMEOUT, retry: callback})
        }), this.ui.timeout);

        Trello.cards.get(ids[0] + '/attachments/' + ids[1], mxUtils.bind(this, function (meta) {
            window.clearTimeout(timeoutThread);

            if (acceptResponse) {
                var binary = /\.png$/i.test(meta.name);

                // TODO Trello doesn't allow CORS requests to load attachments. Confirm that
                // and make sure that only a proxy technique can work!
                // Handles .vsdx, Gliffy and PNG+XML files by creating a temporary file
                if (/\.v(dx|sdx?)$/i.test(meta.name) || /\.gliffy$/i.test(meta.name) ||
                    (!this.ui.useCanvasForExport && binary)) {
                    this.ui.convertFile(PROXY_URL + '?url=' + encodeURIComponent(meta.url), meta.name, meta.mimeType,
                        this.extension, success, error);
                } else {
                    acceptResponse = true;

                    timeoutThread = window.setTimeout(mxUtils.bind(this, function () {
                        acceptResponse = false;
                        error({code: App.ERROR_TIMEOUT})
                    }), this.ui.timeout);

                    this.ui.editor.loadUrl(PROXY_URL + '?url=' + encodeURIComponent(meta.url), mxUtils.bind(this, function (data) {
                        window.clearTimeout(timeoutThread);

                        if (acceptResponse) {
                            //keep our id which includes the cardId
                            meta.compoundId = id;

                            var index = (binary) ? data.lastIndexOf(',') : -1;

                            if (index > 0) {
                                var xml = this.ui.extractGraphModelFromPng(data);

                                if (xml != null && xml.length > 0) {
                                    data = xml;
                                } else {
                                    // TODO: Import PNG
                                }
                            }

                            if (asLibrary) {
                                success(new TrelloLibrary(this.ui, data, meta));
                            } else {
                                success(new TrelloFile(this.ui, data, meta));
                            }
                        }
                    }), mxUtils.bind(this, function (err, req) {
                        window.clearTimeout(timeoutThread);

                        if (acceptResponse) {
                            if (req.status == 401) {
                                this.authenticate(callback, error, true);
                            } else {
                                error();
                            }
                        }
                    }), binary || (meta.mimeType != null &&
                        meta.mimeType.substring(0, 6) == 'image/'));
                }
            }
        }), mxUtils.bind(this, function (err) {
            window.clearTimeout(timeoutThread);

            if (acceptResponse) {
                if (err != null && err.status == 401) {
                    this.authenticate(callback, error, true);
                } else {
                    error();
                }
            }
        }));
    });

    this.authenticate(callback, error);
};

/**
 *
 */
TrelloClient.prototype.insertLibrary = function (filename, data, success, error, cardId) {
    this.insertFile(filename, data, success, error, true, cardId);
};

/**
 *
 */
TrelloClient.prototype.insertFile = function (filename, data, success, error, asLibrary, cardId) {
    asLibrary = (asLibrary != null) ? asLibrary : false;

    var callback = mxUtils.bind(this, function () {
        var fn = mxUtils.bind(this, function (fileData) {
            this.writeFile(filename, fileData, cardId, mxUtils.bind(this, function (meta) {
                if (asLibrary) {
                    success(new TrelloLibrary(this.ui, data, meta));
                } else {
                    success(new TrelloFile(this.ui, data, meta));
                }
            }), error);
        });

        if (this.ui.useCanvasForExport && /(\.png)$/i.test(filename)) {
            this.ui.getEmbeddedPng(mxUtils.bind(this, function (pngData) {
                fn(this.ui.base64ToBlob(pngData, 'image/png'));
            }), error, data);
        } else {
            fn(data);
        }
    });

    this.authenticate(callback, error);
};

/**
 *
 */
TrelloClient.prototype.saveFile = function (file, success, error) {
    // write the file first (with the same name), then delete the old file
    // so that nothing is lost if something goes wrong with deleting
    var ids = file.meta.compoundId.split(this.SEPARATOR);

    var fn = mxUtils.bind(this, function (data) {
        this.writeFile(file.meta.name, data, ids[0], function (meta) {
            Trello.del('cards/' + ids[0] + '/attachments/' + ids[1], mxUtils.bind(this, function () {
                success(meta);
            }), mxUtils.bind(this, function (err) {
                if (err != null && err.status == 401) {
                    // KNOWN: Does not wait for popup to close for callback
                    this.authenticate(callback, error, true);
                } else {
                    error();
                }
            }));
        }, error);
    });

    var callback = mxUtils.bind(this, function () {
        if (this.ui.useCanvasForExport && /(\.png)$/i.test(file.meta.name)) {
            this.ui.getEmbeddedPng(mxUtils.bind(this, function (data) {
                fn(this.ui.base64ToBlob(data, 'image/png'));
            }), error, (this.ui.getCurrentFile() != file) ? file.getData() : null);
        } else {
            fn(file.getData());
        }
    });

    this.authenticate(callback, error);
};

/**
 *
 */
TrelloClient.prototype.writeFile = function (filename, data, cardId, success, error) {
    if (filename != null && data != null) {
        if (data.length >= this.maxFileSize) {
            error({
                message: mxResources.get('drawingTooLarge') + ' (' +
                    this.ui.formatFileSize(data.length) + ' / 10 MB)'
            });

            return;
        }

        var fn = mxUtils.bind(this, function () {
            var acceptResponse = true;

            var timeoutThread = window.setTimeout(mxUtils.bind(this, function () {
                acceptResponse = false;
                error({code: App.ERROR_TIMEOUT, retry: fn});
            }), this.ui.timeout);

            var formData = new FormData();
            formData.append('key', Trello.key());
            formData.append('token', Trello.token());
            formData.append('file', typeof data === 'string' ? new Blob([data]) : data, filename);
            formData.append('name', filename);

            var request = new XMLHttpRequest();
            request.responseType = 'json';

            request.onreadystatechange = mxUtils.bind(this, function () {
                if (request.readyState === 4) {
                    window.clearTimeout(timeoutThread);

                    if (acceptResponse) {
                        if (request.status == 200) {
                            var fileMeta = request.response;
                            fileMeta.compoundId = cardId + this.SEPARATOR + fileMeta.id
                            success(fileMeta);
                        } else if (request.status == 401) {
                            this.authenticate(fn, error, true);
                        } else {
                            error();
                        }
                    }
                }
            });

            request.open('POST', this.baseUrl + 'cards/' + cardId + '/attachments');
            request.send(formData);
        });

        this.authenticate(fn, error);
    } else {
        error({message: mxResources.get('unknownError')});
    }
};

/**
 * Checks if the client is authorized and calls the next step.
 */
TrelloClient.prototype.pickLibrary = function (fn) {
    this.pickFile(fn);
};

/**
 *
 */
TrelloClient.prototype.pickFolder = function (fn) {
    this.authenticate(mxUtils.bind(this, function () {
        // show file select
        this.showTrelloDialog(false, fn);
    }), mxUtils.bind(this, function (e) {
        this.ui.showError(mxResources.get('error'), e);
    }));
};

/**
 * Checks if the client is authorized and calls the next step.
 */
TrelloClient.prototype.pickFile = function (fn, returnObject) {
    fn = (fn != null) ? fn : mxUtils.bind(this, function (id) {
        this.ui.loadFile('T' + encodeURIComponent(id));
    });

    this.authenticate(mxUtils.bind(this, function () {
        // show file select
        this.showTrelloDialog(true, fn);
    }), mxUtils.bind(this, function (e) {
        this.ui.showError(mxResources.get('error'), e, mxResources.get('ok'));
    }));
};


/**
 *
 */
TrelloClient.prototype.showTrelloDialog = function (showFiles, fn) {
    var cardId = null;
    var filter = '@me';
    var linkCounter = 0;

    var content = document.createElement('div');
    content.style.whiteSpace = 'nowrap';
    content.style.overflow = 'hidden';
    content.style.height = '224px';

    var hd = document.createElement('h3');
    mxUtils.write(hd, showFiles ? mxResources.get('selectFile') : mxResources.get('selectCard'));
    hd.style.cssText = 'width:100%;text-align:center;margin-top:0px;margin-bottom:12px';
    content.appendChild(hd);

    var div = document.createElement('div');
    div.style.whiteSpace = 'nowrap';
    div.style.overflow = 'auto';
    div.style.height = '194px';
    content.appendChild(div);

    var dlg = new CustomDialog(this.ui, content);
    this.ui.showDialog(dlg.container, 340, 270, true, true);

    dlg.okButton.parentNode.removeChild(dlg.okButton);

    var createLink = mxUtils.bind(this, function (label, fn, preview) {
        linkCounter++;
        var div = document.createElement('div');
        div.style = 'width:100%;text-overflow:ellipsis;overflow:hidden;vertical-align:middle;' +
            'padding:2px 0 2px 0;background:' + (linkCounter % 2 == 0 ?
                ((uiTheme == 'dark') ? '#000' : '#eee') :
                ((uiTheme == 'dark') ? '' : '#fff'));
        var link = document.createElement('a');
        link.style.cursor = 'pointer';

        if (preview != null) {
            var img = document.createElement('img');
            img.src = preview.url;
            img.width = preview.width;
            img.height = preview.height;
            img.style = "border: 1px solid black;margin:5px;vertical-align:middle"
            link.appendChild(img);
        }

        mxUtils.write(link, label);
        mxEvent.addListener(link, 'click', fn);

        div.appendChild(link);

        return div;
    });

    var error = mxUtils.bind(this, function (err) {
        this.ui.handleError(err, null, mxUtils.bind(this, function () {
            this.ui.spinner.stop();
            this.ui.hideDialog();
        }));
    });

    var selectAtt = mxUtils.bind(this, function () {
        linkCounter = 0;
        div.innerHTML = '';
        this.ui.spinner.spin(div, mxResources.get('loading'));

        var callback = mxUtils.bind(this, function () {
            Trello.cards.get(cardId + '/attachments', {fields: 'id,name,previews'}, mxUtils.bind(this, function (data) {
                    this.ui.spinner.stop();
                    var files = data;
                    div.appendChild(createLink('../ [Up]', mxUtils.bind(this, function () {
                        selectCard();
                    })));
                    mxUtils.br(div);

                    if (files == null || files.length == 0) {
                        mxUtils.write(div, mxResources.get('noFiles'));
                    } else {
                        var listFiles = mxUtils.bind(this, function () {
                            for (var i = 0; i < files.length; i++) {
                                (mxUtils.bind(this, function (file) {
                                    div.appendChild(createLink(file.name, mxUtils.bind(this, function () {
                                        this.ui.hideDialog();
                                        fn(cardId + this.SEPARATOR + file.id);
                                    }), file.previews != null ? file.previews[0] : null));
                                }))(files[i]);
                            }
                        });

                        listFiles();
                    }
                }),
                mxUtils.bind(this, function (req) {
                    if (req.status == 401) {
                        this.authenticate(callback, error, true);
                    } else if (error != null) {
                        error(req);
                    }
                }));
        });

        callback();
    });

    // Adds paging for cards (files limited to 1000 by API)
    var pageSize = 100;
    var nextPageDiv = null;
    var scrollFn = null;

    var selectCard = mxUtils.bind(this, function (page) {
        if (page == null) {
            linkCounter = 0;
            div.innerHTML = '';
            page = 1;
        }

        this.ui.spinner.spin(div, mxResources.get('loading'));

        if (nextPageDiv != null && nextPageDiv.parentNode != null) {
            nextPageDiv.parentNode.removeChild(nextPageDiv);
        }

        nextPageDiv = document.createElement('a');
        nextPageDiv.style.display = 'block';
        nextPageDiv.style.cursor = 'pointer';
        mxUtils.write(nextPageDiv, mxResources.get('more') + '...');

        var nextPage = mxUtils.bind(this, function () {
            mxEvent.removeListener(div, 'scroll', scrollFn);
            selectCard(page + 1);
        });

        mxEvent.addListener(nextPageDiv, 'click', nextPage);

        var callback = mxUtils.bind(this, function () {
            Trello.get('search', {
                    'query': (mxUtils.trim(filter) == '') ? 'is:open' : filter,
                    'cards_limit': pageSize,
                    'cards_page': page - 1
                },
                mxUtils.bind(this, function (data) {
                    this.ui.spinner.stop();
                    var cards = (data != null) ? data.cards : null;

                    if (cards == null || cards.length == 0) {
                        mxUtils.write(div, mxResources.get('noFiles'));
                    } else {
                        if (page == 1) {
                            div.appendChild(createLink(mxResources.get('filterCards') + '...', mxUtils.bind(this, function () {
                                var dlg = new FilenameDialog(this.ui, filter, mxResources.get('ok'), mxUtils.bind(this, function (value) {
                                    if (value != null) {
                                        filter = value;
                                        selectCard();
                                    }
                                }), mxResources.get('filterCards'), null, null, 'http://help.trello.com/article/808-searching-for-cards-all-boards');
                                this.ui.showDialog(dlg.container, 300, 80, true, false);
                                dlg.init();
                            })));

                            mxUtils.br(div);
                        }

                        for (var i = 0; i < cards.length; i++) {
                            (mxUtils.bind(this, function (card) {
                                div.appendChild(createLink(card.name, mxUtils.bind(this, function () {
                                    if (showFiles) {
                                        cardId = card.id;
                                        selectAtt();
                                    } else {
                                        this.ui.hideDialog();
                                        fn(card.id);
                                    }
                                })));
                            }))(cards[i]);
                        }

                        if (cards.length == pageSize) {
                            div.appendChild(nextPageDiv);

                            scrollFn = function () {
                                if (div.scrollTop >= div.scrollHeight - div.offsetHeight) {
                                    nextPage();
                                }
                            };

                            mxEvent.addListener(div, 'scroll', scrollFn);
                        }
                    }
                }),
                mxUtils.bind(this, function (req) {
                    if (req.status == 401) {
                        this.authenticate(callback, error, true);
                    } else if (error != null) {
                        error({message: req.responseText});
                    }
                }));
        });

        callback();
    });

    selectCard();
};

/**
 * Checks if the client is authorized
 */
TrelloClient.prototype.isAuthorized = function () {
    //TODO this may break if Trello client.js is changed
    try {
        return localStorage['trello_token'] != null; //Trello.authorized(); doesn't work unless authorize is called first
    } catch (e) {
        // ignores access denied
    }

    return false;
};


/**
 * Logout and deauthorize the user.
 */
TrelloClient.prototype.logout = function () {
    localStorage.removeItem('trello_token');
    Trello.deauthorize();
};
